"use client"; import { useEffect, useState, use } from "react"; import { getFirestore, doc, getDoc, collection, query, where, onSnapshot, orderBy, } from "firebase/firestore"; import { Card, CardBody, Button, Spinner, Input, addToast, Chip, Divider, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, useDisclosure, Slider, Image, } from "@heroui/react"; import { Link } from "@heroui/link"; import { motion, AnimatePresence } from "framer-motion"; import { fontCursive, fontSans, fontMono } from "@/config/fonts"; import firebaseApp from "@/config/firebase"; import { HeartFilledIcon, CheckIcon, MapPinIcon } from "@/components/icons"; import { addContribution } from "../actions"; const db = getFirestore(firebaseApp()); interface RegistryItem { id: string; name: string; category: string; price: number; groupAllowed: boolean; totalContributed: number; status: "available" | "completed"; imageUrl?: string; storeUrl?: string; } interface Contribution { id: string; guestName: string; amount: number; timestamp: any; itemName: string; } export default function GuestRegistryPage({ params, }: { params: Promise<{ slug: string }>; }) { const { slug } = use(params); const [guest, setGuest] = useState(null); const [loading, setLoading] = useState(true); const [notFound, setNotFound] = useState(false); const [items, setItems] = useState([]); const [contributions, setContributions] = useState([]); const [selectedCategory, setSelectedCategory] = useState("All"); const { isOpen, onOpen, onOpenChange } = useDisclosure(); const [selectedItem, setSelectedItem] = useState(null); const [contribAmount, setContribAmount] = useState(1000); const [captcha, setCaptcha] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { const fetchGuest = async () => { try { const docRef = doc(db, "invitation", slug); const docSnap = await getDoc(docRef); if (docSnap.exists()) { setGuest(docSnap.data()); } else { setNotFound(true); } } catch (error) { console.error("Fetch error:", error); setNotFound(true); } finally { setLoading(false); } }; if (slug) fetchGuest(); // Listen to items const qItems = query(collection(db, "registry_items"), orderBy("category")); const unsubscribeItems = onSnapshot(qItems, (snapshot) => { const itemsData = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })) as RegistryItem[]; setItems(itemsData); }); // Listen to contributions for the Wall of Generosity const qContribs = query(collection(db, "registry_contributions"), orderBy("timestamp", "desc")); const unsubscribeContribs = onSnapshot(qContribs, (snapshot) => { const contribsData = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })) as Contribution[]; setContributions(contribsData); }); return () => { unsubscribeItems(); unsubscribeContribs(); }; }, [slug]); const handleContribute = async (onClose: () => void) => { if (!selectedItem) return; if (captcha !== "10") { addToast({ title: "Security Check", description: "Answer correctly!", color: "warning" }); return; } if (contribAmount <= 0) { addToast({ title: "Invalid Amount", description: "Please enter a valid amount.", color: "danger" }); return; } const remaining = (selectedItem.price ?? 0) - (selectedItem.totalContributed ?? 0); if (contribAmount > remaining && !selectedItem.groupAllowed) { addToast({ title: "Amount exceeded", description: `You can only contribute up to ₹${remaining}.`, color: "danger" }); return; } setIsSubmitting(true); try { const result = await addContribution({ itemId: selectedItem.id, itemName: selectedItem.name, slug, guestName: guest.name, amount: contribAmount, }); if (result.success) { addToast({ title: "Pledge Recorded!", description: `We've recorded your gift of ₹${contribAmount} for ${selectedItem.name}. We don't process payments ourselves, so please send the money with generosity by UPI on the next page.`, color: "success" }); onClose(); setCaptcha(""); // Open sagun in a new tab window.open('/sagun', '_blank'); } else { throw new Error(result.error || "Failed to process contribution."); } } catch (error: any) { console.error("Contribution error", error); addToast({ title: "Error", description: error.message || "Failed to process contribution.", color: "danger" }); } finally { setIsSubmitting(false); } }; if (loading) return
; if (notFound) { return (

Invitation Not Found

We couldn't find an invitation for this link.

); } return (

Wedding Registry

Gifts of Love

Hi {guest.name}, your presence is our greatest gift. However, if you wish to honor us with a gift, we have curated a list of things we'd love for our new home.

{/* Overall Progress Tracker */}

Registry Completion

{Math.round((items.reduce((acc, i) => acc + (i.totalContributed ?? 0), 0) / (items.reduce((acc, i) => acc + (i.price ?? 0), 0) || 1)) * 100)}% Funded

₹{items.reduce((acc, i) => acc + (i.totalContributed ?? 0), 0).toLocaleString()} / ₹{items.reduce((acc, i) => acc + (i.price ?? 0), 0).toLocaleString()}

acc + (i.totalContributed ?? 0), 0) / (items.reduce((acc, i) => acc + (i.price ?? 0), 0) || 1)) * 100}%` }} className="h-full bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 rounded-full" />
{/* Category Filters */}
{["All", ...Array.from(new Set(items.map(i => i.category)))].map(cat => ( setSelectedCategory(cat)} > {cat} ))}
{items.filter(item => selectedCategory === "All" || item.category === selectedCategory).length === 0 ? (

🎁

{items.length === 0 ? "Our registry is being curated" : "No items in this category"}

{items.length === 0 ? "Check back soon as we add items for our new home." : "Try selecting another category to see more gifts."}

{items.length > 0 && selectedCategory !== "All" && ( )}
) : ( items .filter(item => selectedCategory === "All" || item.category === selectedCategory) .map((item) => { const isCompleted = (item.totalContributed ?? 0) >= (item.price ?? 0); const progress = Math.min(100, ((item.totalContributed ?? 0) / (item.price || 1)) * 100); return ( {item.imageUrl && (
{item.name}
)}
{item.category} {isCompleted && ( } className="font-bold"> Completed )}

{item.name}

{item.groupAllowed ? "Multiple guests can contribute to this gift." : "A single contribution gift."}

{item.storeUrl && ( )}
Goal: ₹{(item.price ?? 0).toLocaleString()} {Math.round(progress)}%

₹{(item.totalContributed ?? 0).toLocaleString()} contributed so far

); }))}

Wall of Generosity

{contributions.length === 0 ? (

Be the first one to appear here!

) : ( contributions.map((c, i) => (

{c.guestName}

Gifted toward {c.itemName}

₹{(c.amount ?? 0).toLocaleString()}

{c.timestamp?.toDate() ? new Date(c.timestamp.toDate()).toLocaleDateString() : "Today"}

)) )}

Thank You

Your generosity helps us build our future together. We are truly grateful for your love and support.

{(onClose) => ( <> Contribute to {selectedItem?.name} {selectedItem?.imageUrl && (
{selectedItem.name}
)}

Target Price

₹{(selectedItem?.price ?? 0).toLocaleString()}

Remaining

₹{((selectedItem?.price ?? 0) - (selectedItem?.totalContributed ?? 0)).toLocaleString()}

Choose your contribution amount

{selectedItem?.groupAllowed ? ( <> setContribAmount(val as number)} classNames={{ label: "font-black text-wedding-pink-600", value: "font-mono font-bold" }} /> setContribAmount(Number(e.target.value))} variant="bordered" classNames={{ input: "outline-none" }} /> ) : (

Total Gift Amount

₹{(selectedItem?.price ?? 0).toLocaleString()}

)}

Bot Protection

setCaptcha(e.target.value)} />
)}
); }